﻿// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DXGI;
using SharpDX.Samples;
using SharpDX.DirectWrite;
using SharpDX.WIC;

using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using Bitmap = SharpDX.Direct2D1.Bitmap;
using PixelFormat = SharpDX.Direct2D1.PixelFormat;

using FlyCapture2Managed;
using FlyCapture2Managed.Gui;


namespace BitmapApp
{
    /// <summary>
    /// This sample demonstrates how to load a Direct2D1 bitmap from a file.
    /// This method will be part of a future version of SharpDX API.
    /// </summary>
    public class Program : Direct2D1DemoApp
    {
        private Bitmap _bitmap, _bitmapLesa;
        string FullText = "";
        public TextFormat TextFormat { get; private set; }
        public TextFormat LesaTextFormat;
        public TextLayout TextLayout { get; private set; }
        public ManagedCameraBase m_camera = null;
        public ManagedImage m_processedImage;
        private DataStream ds;
        public System.Drawing.Rectangle sourceArea;
        BitmapProperties bitmapProperties;
        Size2 size;
        int stride;
        BitmapData bitmapData;
        DemoConfiguration DC;
        public System.Drawing.Bitmap BitmapToDraw;
        public System.Drawing.Bitmap BitmapToSave;
        public string Text { get; set; }
        public System.Drawing.Bitmap LesaBitmap = null;
        public string LesaText = "";
        public System.Drawing.Font ImgFont = new System.Drawing.Font("Calibri", 64);
        public System.Drawing.Color FontColor = System.Drawing.Color.Yellow;
        public bool FlipY180 = false;

        protected override void Initialize(DemoConfiguration demoConfiguration)
        {
            base.Initialize(demoConfiguration);
            DC = demoConfiguration;
            TextFormat = new TextFormat(FactoryDWrite, ImgFont.OriginalFontName, ImgFont.SizeInPoints/2) { TextAlignment = TextAlignment.Leading, ParagraphAlignment = ParagraphAlignment.Near };
            LesaTextFormat = new TextFormat(FactoryDWrite, ImgFont.OriginalFontName, ImgFont.SizeInPoints/2) { TextAlignment = TextAlignment.Leading, ParagraphAlignment = ParagraphAlignment.Near };
            UpdateConcentrationFontColor(FontColor);
            
            RenderTarget2D.TextAntialiasMode = TextAntialiasMode.Cleartype;
            Text = "";

            ds = new DataStream((int)((int)m_processedImage.rows*5120/*(m_processedImage.stride+(int)m_processedImage.cols)*/), true, true);
            sourceArea = new System.Drawing.Rectangle(0, 0, (int)m_processedImage.cols, (int)m_processedImage.rows);
            bitmapProperties = new BitmapProperties(new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied));
            size = new Size2((int)m_processedImage.cols, (int)m_processedImage.rows);
            stride = (int)m_processedImage.cols * sizeof(int);
            int cc = FontColor.ToArgb();

            
        }

        protected override void Draw(DemoTime time)
        {
            base.Draw(time);
            if (BitmapToDraw != null)
            {
                SysDrawningBitmapToSharpDXBitmap(BitmapToDraw);
                FullText = Text;
                if (FlipY180)  RenderTarget2D.Transform = Matrix3x2.Translation(-BitmapToDraw.Width, -BitmapToDraw.Height) * Matrix3x2.Rotation((float)Math.PI);// Matrix3x2.Add(Matrix3x2.Rotation((float)0), Matrix3x2.Translation(500, 100));
                
                RenderTarget2D.DrawBitmap(_bitmap, new RectangleF(0, 0, DC.Width, DC.Height), 1.0f, SharpDX.Direct2D1.BitmapInterpolationMode.Linear);
                
                RenderTarget2D.Transform = Matrix3x2.Identity;
                 if (LesaBitmap != null) try
                     {
                         SysDrawningBitmapToSharpDXBitmapLesa(LesaBitmap);
                         RenderTarget2D.DrawBitmap(_bitmapLesa, new RectangleF(DC.Width - LesaBitmap.Width, DC.Height - LesaBitmap.Height, DC.Width, DC.Height), 1.0f, SharpDX.Direct2D1.BitmapInterpolationMode.Linear);
                         _bitmapLesa.Dispose();

                         TextLayout = new TextLayout(FactoryDWrite, LesaText, LesaTextFormat, DC.Width, DC.Height);
                            
                         RenderTarget2D.DrawTextLayout(new Vector2(0, DC.Height - ImgFont.SizeInPoints / 2), TextLayout, SceneColorBrush, DrawTextOptions.None);
                     }
                     catch { }
                
                
                TextLayout = new TextLayout(FactoryDWrite, FullText, TextFormat, sourceArea.Width, sourceArea.Height);
                RenderTarget2D.DrawTextLayout(new Vector2(0, 0), TextLayout, SceneColorBrush, DrawTextOptions.Clip);


                _bitmap.Dispose();
            }


        }
        public void UpdateConcentrationFontColor(System.Drawing.Color color)
        {
            SceneColorBrush.Color = Color.FromBgra(BitConverter.ToInt32(BitConverter.GetBytes(color.ToArgb()), 0));
        }

        public System.Drawing.Color GetConcentrationColor()
        {
            if (SceneColorBrush != null)
                return System.Drawing.Color.FromArgb((int)(SceneColorBrush.Color.Red * 255f), (int)(SceneColorBrush.Color.Green * 255f), (int)(SceneColorBrush.Color.Blue * 255f));
            else
                return System.Drawing.Color.Yellow;
        }
        SharpDX.WIC.ImagingFactory wicFactory;
        SharpDX.Direct2D1.Factory d2dFactory;
        SharpDX.WIC.Bitmap wicBitmap;
        WicRenderTarget d2dRenderTarget;
        SharpDX.DirectWrite.Factory FactoryDWrite1;
        SolidColorBrush SceneColorBrush1;
        TextLayout TextLayout1;
        WICStream stream;
        JpegBitmapEncoder encoder;
        BitmapFrameEncode bitmapFrameEncode;
        public void SaveCapture(string FileName)
        {
            wicFactory = new ImagingFactory();
            d2dFactory = new SharpDX.Direct2D1.Factory();
            
            wicBitmap = new SharpDX.WIC.Bitmap(wicFactory, DC.Width, DC.Height, SharpDX.WIC.PixelFormat.Format32bppBGR, BitmapCreateCacheOption.CacheOnLoad);

            var renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default, new PixelFormat(Format.Unknown, AlphaMode.Unknown), 0, 0, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT);

            d2dRenderTarget = new WicRenderTarget(d2dFactory, wicBitmap, renderTargetProperties);

            FactoryDWrite1 = new SharpDX.DirectWrite.Factory();
            SceneColorBrush1 = new SolidColorBrush(d2dRenderTarget, Color.White);
            SceneColorBrush1.Color = SceneColorBrush.Color;

            d2dRenderTarget.BeginDraw();

            if (FlipY180) d2dRenderTarget.Transform = Matrix3x2.Translation(-BitmapToDraw.Width, -BitmapToDraw.Height) * Matrix3x2.Rotation((float)Math.PI);// Matrix3x2.Add(Matrix3x2.Rotation((float)0), Matrix3x2.Translation(500, 100));
            d2dRenderTarget.DrawBitmap(WinBtmap2DXBitmap(BitmapToSave,d2dRenderTarget),new RectangleF(0, 0, DC.Width, DC.Height), 1.0f, SharpDX.Direct2D1.BitmapInterpolationMode.Linear);
            RenderTarget2D.Transform = Matrix3x2.Identity;
            
            bool CopyDone = false;
            if (LesaBitmap != null)
            {
                while (!CopyDone)
                    try
                    {


                        d2dRenderTarget.DrawBitmap(WinBtmap2DXLesaBitmap(LesaBitmap, d2dRenderTarget), new RectangleF(DC.Width - LesaBitmap.Width, DC.Height - LesaBitmap.Height, DC.Width, DC.Height), 1.0f, SharpDX.Direct2D1.BitmapInterpolationMode.Linear);

                        TextLayout1 = new TextLayout(FactoryDWrite1, LesaText, LesaTextFormat, DC.Width, DC.Height);
                        d2dRenderTarget.DrawTextLayout(new Vector2(0, DC.Height - ImgFont.SizeInPoints / 2), TextLayout1, SceneColorBrush1, DrawTextOptions.None);
                        CopyDone = true;
                    }

                    catch { }
            }

            TextLayout1 = new TextLayout(FactoryDWrite1, Text, TextFormat, DC.Width, DC.Height);
            d2dRenderTarget.DrawTextLayout(new Vector2(0, 0), TextLayout1, SceneColorBrush1, DrawTextOptions.None);
            
            
            d2dRenderTarget.EndDraw();

            stream = new WICStream(wicFactory, FileName, SharpDX.IO.NativeFileAccess.Write);
            // Initialize a Jpeg encoder with this stream
            encoder = new JpegBitmapEncoder(wicFactory);
            encoder.Initialize(stream);

            // Create a Frame encoder
            bitmapFrameEncode = new BitmapFrameEncode(encoder);
            bitmapFrameEncode.Initialize();
            bitmapFrameEncode.SetSize(DC.Width, DC.Height);
            var pixelFormatGuid = SharpDX.WIC.PixelFormat.FormatDontCare;
            bitmapFrameEncode.SetPixelFormat(ref pixelFormatGuid);
            bitmapFrameEncode.WriteSource(wicBitmap);

            bitmapFrameEncode.Commit();
            encoder.Commit();

            bitmapFrameEncode.Dispose();
            encoder.Dispose();
            stream.Dispose();
            TextLayout1.Dispose();
            SceneColorBrush1.Dispose();
            FactoryDWrite1.Dispose();
            d2dRenderTarget.Dispose();
            wicBitmap.Dispose();
            d2dFactory.Dispose();
            wicFactory.Dispose();
            GC.Collect();

        }
        
        private Bitmap WinBtmap2DXBitmap(System.Drawing.Bitmap bitmap, RenderTarget rt)
        {
            BitmapData bitmapData1 = bitmap.LockBits(sourceArea, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
            try
            {
                ds.WriteRange(bitmapData1.Scan0, bitmapData1.Stride * sourceArea.Height);
            }
            catch { }
            bitmap.UnlockBits(bitmapData1);
            ds.Position = 0;
            //Attempted to read or write protected memory. This is often an indication that other memory is corrupt. При сохранении картинок
            //HRESULT: [0x8007000E], Module: [General], ApiCode: [E_OUTOFMEMORY/Out of memory], Message: Недостаточно памяти для завершения операции.
            return new Bitmap(rt, size, ds, bitmapData1.Stride, bitmapProperties);
        }
        private Bitmap WinBtmap2DXLesaBitmap(System.Drawing.Bitmap bitmap,RenderTarget rt)
        {
            BitmapData bitmapData1 = bitmap.LockBits(new System.Drawing.Rectangle(0,0,LesaBitmap.Width,LesaBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
            try
            {
                ds.WriteRange(bitmapData1.Scan0, bitmapData1.Stride * bitmap.Height);
            }
            catch { }
            bitmap.UnlockBits(bitmapData1);
            ds.Position = 0;
            
            return new Bitmap(rt, new Size2(DC.Width, DC.Height), ds, bitmapData1.Stride, bitmapProperties);
        }
        private void SysDrawningBitmapToSharpDXBitmap(System.Drawing.Bitmap bitmap)
        {
            bitmapData = bitmap.LockBits(sourceArea, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
            //Out of memory.
            //HUGE FUCKING BUG IS HERE - FREEZES WHEN FRAME SAVE HAPPENS
            //   !!!!!!!!!!!!!!!  WARNING !!!!!!!!!!!!!!!!!!!!!
            try
            {
                ds.WriteRange(bitmapData.Scan0, bitmapData.Stride * sourceArea.Height);
            }
            catch { }

            bitmap.UnlockBits(bitmapData);
            ds.Position = 0;

            _bitmap = new Bitmap(RenderTarget2D, size, ds, bitmapData.Stride, bitmapProperties);
        }
        private void SysDrawningBitmapToSharpDXBitmapLesa(System.Drawing.Bitmap bitmap)
        {
            
            BitmapData bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0,0,bitmap.Width,bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
            try
            {
                ds.WriteRange(bitmapData.Scan0, bitmapData.Stride * bitmap.Height);
            }
            catch(Exception ex){}

            bitmap.UnlockBits(bitmapData);
            ds.Position = 0;

            _bitmapLesa = new Bitmap(RenderTarget2D, new Size2(DC.Width, DC.Height), ds, bitmapData.Stride /*stride*/, bitmapProperties);
            ds.Position=0;
            System.GC.Collect();
        }
        public void SourceAreaChanged()
        {
            size = new Size2(sourceArea.Width, sourceArea.Height);
        }

        [STAThread]
        static void Main(string[] args)
        {
            Program program = new Program();
            program.Run(new DemoConfiguration("SharpDX Dual Channel"));
        }
    }
}
